#include "writers.h"



namespace DB_NAME {
namespace wal {

Writer::Writer(WritableFile *file) 
 : file_(file), pos_(0)
{}

RC Writer::write_entry(RecordType type, const char *data, uint32_t size)
{
  RC ret = RC::SUCCESS;
  EntryHeader head = {0, size, type};
  head.checksum = crc32c::Extend(kWalCrcBase[type], 
                                 (uint8_t *)&head.meta, 
                                 sizeof(head.meta));
  if (OP_FAIL(file_->Append(SView((const char *)&head, sizeof(head))))) {
    GLOB_LOG_ERROR("Fail to append entry head. ret={}", KR(ret));
  } else if (OP_FAIL(file_->Append(SView(data, size)))) {
    GLOB_LOG_ERROR("Fail to append data. ret={}", KR(ret));
  } else {
    pos_ += size + kHeaderSize;
  }
  return ret;
}

RC Writer::AddRecord(const SView &data_view) 
{
  RC ret = RC::SUCCESS;
  const char *data = data_view.data();
  size_t size = data_view.size();
  bool begin = true;

  do {
    size_t left = kBlockSize - pos_;
    if (left < kHeaderSize && left > 0) {
      if (OP_FAIL(file_->Append(SView("\x00\x00\x00\x00\x00\x00", left)))) {
        GLOB_LOG_ERROR("Fail to append data int file. ret={}", KR(ret));
        break;
      } else {
        pos_ = 0;
      }
    }

    size_t copy_size = std::min(size, kBlockSize - pos_ - kHeaderSize);
    bool end = size <= copy_size;
    RecordType type;
    if (begin && end) {
      type = RecordType::kTypeFull;
    } else if (begin) {
      type = RecordType::kTypeFirst;
    } else if (end) {
      type = RecordType::kTypeLast;
    } else {
      type = RecordType::kTypeMiddle;
    }

    if (OP_FAIL(write_entry(type, data, static_cast<uint32_t>(copy_size)))) {
      GLOB_LOG_ERROR("Fail to write entry, ret={}", KR(ret));
    } else {
      data += copy_size;
      size -= copy_size;
      begin = false;
    }

  } while(IS_SUCC && size > 0);
  return ret;
}

}
}
